package com.vdong.common.oauth.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.sd4324530.fastweixin.api.ComponentOauthAPI;
import com.github.sd4324530.fastweixin.api.DomainAPI;
import com.github.sd4324530.fastweixin.api.OpenAccountAPI;
import com.github.sd4324530.fastweixin.api.config.AuthorizerApiConfig;
import com.github.sd4324530.fastweixin.api.request.ModifyDomainRequest;
import com.github.sd4324530.fastweixin.api.response.BaseResponse;
import com.github.sd4324530.fastweixin.api.response.GetUserInfoResponse;
import com.github.sd4324530.fastweixin.api.response.OauthGetTokenResponse;
import com.github.sd4324530.fastweixin.api.response.OpenAccountResponse;
import com.github.sd4324530.fastweixin.util.SignUtil;
import com.vdong.common.oauth.common.exception.WebBizException;
import com.vdong.common.oauth.common.pojo.WechatJsConfig;
import com.vdong.common.oauth.common.util.AbstractOpenPlatformSupport;
import com.vdong.common.oauth.entity.mo.WechatAuthorizerInfo;
import com.vdongchina.boot.wechat.util.WechatUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Optional;

/**
 * @program: kpay-basics-api
 * @description: 授权
 * @author: Mr.Zhang
 * @create: 2018-11-13 15:33
 **/
@Slf4j
@Service
@SuppressWarnings({"rawtypes", "unchecked"})
public class WechatAuthorSerivce extends AbstractOpenPlatformSupport {

    private static final String[] REQ_DOMAIN = {"https://app.vdongchina.com",
            "https://studyanimal-test.vdongchina.com",
            "https://meiye-hz.vdongchina.com",
            "https://promote-dev.vdongzc.com",
            "https://jqtest.vdongzc.com",
            "https://studyanimal.vdongchina.com"};

    public void clearAuthorizerCache(String appId) {
        String key = "authorizerApiConfig_" + (appId);
        this.getRedisTemplate().delete(key);
    }

    @Autowired
    private OauthDynamicPropertyService oauthDynamicPropertyService;

    /**
     * 获取微信授权信息（必须是微信认证的，服务号）
     *
     * @param authCode
     * @return
     */
    public WechatAuthorizerInfo getAuthInfoFromWeixin(String authCode) {
        if (StringUtils.isBlank(authCode)) {
            throw new WebBizException("没有获取到授权码，无法授权");
        }
        //使用授权码换取公众号或小程序的接口调用凭据和授权信息
        String authorization = "authorization_info";
        JSONObject authJson = WechatUtil.getQueryAuth(getAppId(), authCode, super.getComponentApiConfig().getAccessToken());
        if (!authJson.containsKey(authorization)) {
            throw new WebBizException("使用授权码换取公众号或小程序的接口调用凭据和授权信息失败,请检查配置项是否正确");
        }
        //授权信息
        JSONObject authorizationInfoJson = authJson.getJSONObject("authorization_info");
        //授权小程序、公众号appId
        String authorizerAppid = authorizationInfoJson.getString("authorizer_appid");
        //String authorizerAccessToken = authorizationInfoJson.getString("authorizer_access_token");//授权方接口调用凭据（在授权的公众号或小程序具备API权限时，才有此返回值），也简称为令牌
        //修改小程序配置，清空缓存
        //clearAuthorizerCache(authorizerAppid);

        String refreshToken = authorizationInfoJson.getString("authorizer_refresh_token");
        String funcInfo = authorizationInfoJson.getString("func_info");
        JSONObject json = WechatUtil.getAuthorizerInfo(getAppId(), authorizerAppid, super.getComponentApiConfig().getAccessToken());
        Boolean isOk = json.getBoolean("success");
        if (!isOk) {
            throw new WebBizException("获取授权信息失败,请检查配置项是否正确");
        }
        //授权账号信息
        String info = "authorizer_info";
        if (!json.containsKey(info)) {
            throw new WebBizException("获取授权信息失败,请检查配置项是否正确");
        }
        JSONObject authorizerInfo = json.getJSONObject("authorizer_info");
        //若授权账号是小程序处理小程序授权详情
        Integer authorizeType = 1;
        String programInfo = "MiniProgramInfo";
        String miniprograminfo = "";
        if (authorizerInfo.containsKey(programInfo)) {
            miniprograminfo = authorizerInfo.getString("MiniProgramInfo");
            authorizeType = 2;
        }
        //授权方昵称
        String nickName = authorizerInfo.getString("nick_name");
        //授权方头像
        String headImg = authorizerInfo.getString("head_img");
        //小程序的原始ID
        String userName = authorizerInfo.getString("user_name");
        //帐号介绍
        String signature = authorizerInfo.getString("signature");
        //授权方公众号所设置的微信号，可能为空
        String alias = authorizerInfo.getString("alias");
        //授权方认证类型，-1代表未认证，0代表微信认证
        Integer verifyTypeInfo = authorizerInfo.getJSONObject("verify_type_info").getInteger("id");
        //授权方公众号类型，0代表订阅号，1代表由历史老帐号升级后的订阅号，2代表服务号
        Integer serviceTypeInfo = authorizerInfo.getJSONObject("service_type_info").getInteger("id");
        Integer intParam = 2;
        if (authorizeType == 1 && !intParam.equals(serviceTypeInfo)) {
            throw new WebBizException("授权方公众号类型只能是服务号，授权失败");
        }
        //小程序的主体名称
        String principalName = authorizerInfo.getString("principal_name");
        //用以了解以下功能的开通状况（0代表未开通，1代表已开通）：
        String businessInfo = authorizerInfo.getString("business_info");
        // open_store:是否开通微信门店功能 open_scan:是否开通微信扫商品功能 open_pay:是否开通微信支付功能 open_card:是否开通微信卡券功能 open_shake:是否开通微信摇一摇功能
        //二维码图片的URL，开发者最好自行也进行保存
        String qrcodeUrl = authorizerInfo.getString("qrcode_url");
        String openId = getOpenPlatformId(authorizerAppid, refreshToken);

        return WechatAuthorizerInfo.builder().appid(authorizerAppid).openId(openId).refreshToken(refreshToken)
                .nickName(nickName).headImg(headImg).userName(userName).alias(alias).signature(signature)
                .qrcodeUrl(qrcodeUrl).verifyTypeInfo(verifyTypeInfo).serviceTypeInfo(serviceTypeInfo)
                .principalName(principalName).businessInfo(businessInfo).miniprograminfo(miniprograminfo)
                .authorizeType(authorizeType).funcInfo(funcInfo).build();
    }

    public GetUserInfoResponse getUserInfo(String appId, String code) {
        log.info("config:" + JSON.toJSONString(super.getComponentApiConfig()));
        ComponentOauthAPI componentOauthAPI = new ComponentOauthAPI(super.getComponentApiConfig());
        OauthGetTokenResponse oauthGetTokenResponse = Optional.ofNullable(componentOauthAPI.getToken(code, appId)).orElseThrow(() -> new WebBizException("获取TOKEN失败"));
        return Optional.ofNullable(componentOauthAPI.getUserInfo(oauthGetTokenResponse.getAccessToken(), oauthGetTokenResponse.getOpenid())).orElseThrow(() -> new WebBizException("获取用户信息失败"));
    }

    /**
     * 修改小程序域名
     *
     * @param appId
     * @param refreshToken
     */
    public void modifyDomain(String appId, String refreshToken) {
        AuthorizerApiConfig config = super.getAuthorizerApiConfig(appId, refreshToken);
        if (config == null) {
            throw new WebBizException("获取授权信息失败，无法修改小程序域名");
        }
        //修改小程序域名
        ModifyDomainRequest modifyDomainRequest = new ModifyDomainRequest();
        //add添加, delete删除, set覆盖, get获取。当参数是get时不需要填四个域名字段
        modifyDomainRequest.setAction("set");
        modifyDomainRequest.setRequestdomain(REQ_DOMAIN);
        modifyDomainRequest.setUploaddomain(new String[]{"https://up.qbox.me"});
        modifyDomainRequest.setDownloaddomain(new String[]{"https://image.vdongchina.com"});
        modifyDomainRequest.setWsrequestdomain(new String[]{"wss://socket.vdongchina.com"});
        DomainAPI domainAPI = new DomainAPI(config);
        BaseResponse modifyDomianResponse = domainAPI.modifyDomain(modifyDomainRequest);
        Boolean modifyResult = domainAPI.isSuccess(modifyDomianResponse.getErrcode());
        if (!modifyResult) {
            throw new WebBizException("修改小程序服务器域名失败:" + modifyDomianResponse.getErrmsg());
        }
    }

    public WechatJsConfig getWechatJsConfig(String appId, String refreshToken, String url) {
        AuthorizerApiConfig config = super.getAuthorizerApiConfig(appId, refreshToken);
        String noncestr = RandomStringUtils.randomAlphanumeric(16);
        String timestamp = System.currentTimeMillis() / 1000 + "";
        String signature = SignUtil.genSignature(config.getJsApiTicket(), noncestr, timestamp, url);
        return WechatJsConfig.builder()
                .appId(appId)
                .nonceStr(noncestr)
                .timestamp(timestamp)
                .signature(signature)
                .build();
    }

    private String getOpenPlatformId(String appId, String refreshToken) {
        OpenAccountAPI api = getOpenAccountAPI(appId, refreshToken);
        OpenAccountResponse response = api.get(appId);
        if (!api.isSuccess(response.getErrcode())) {
            log.error("获取开放平台账号失败:{}", response.getErrmsg());
            //throw new WebBizException("获取开放平台账号失败：" + response.getErrmsg());
            return null;
        }
        return response.getOpenAppId();
    }

    String createOpenPlatform(String appId, String refreshToken) {
        log.debug("createOpenPlatform:{} ,appId:{}", "创建开放平台", appId);
        OpenAccountAPI api = getOpenAccountAPI(appId, refreshToken);
        OpenAccountResponse response = api.create(appId);
        String code = "89000";
        if (!api.isSuccess(response.getErrcode())) {
            if (code.equals(response.getErrcode())) {
                throw new WebBizException("创建开放平台账号失败：该公众号/小程序已经绑定了开放平台帐号");
            }
            throw new WebBizException("创建开放平台账号失败：" + response.getErrmsg());
        }
        return response.getOpenAppId();
    }

    void bindOpenPlatform(String appId, String refreshToken, String openId) {
        log.debug("bindOpenPlatform:{} ,appId:{} ,openId:{}", "绑定开放平台", appId, openId);
        OpenAccountAPI api = getOpenAccountAPI(appId, refreshToken);
        BaseResponse response = api.bind(appId, openId);
        String code1 = "89000";
        String code2 = "89001";
        String code3 = "89003";
        String code4 = "89004";
        if (!api.isSuccess(response.getErrcode())) {
            if (code1.equals(response.getErrcode())) {
                throw new WebBizException("绑定开放平台账号失败：该公众号/小程序已经绑定了开放平台帐号");
            }
            if (code2.equals(response.getErrcode())) {
                throw new WebBizException("绑定开放平台账号失败：授权信息与开放平台帐号主体不相同");
            }
            if (code3.equals(response.getErrcode())) {
                throw new WebBizException("绑定开放平台账号失败：该开放平台帐号并非通过api创建，不允许操作");
            }
            if (code4.equals(response.getErrcode())) {
                throw new WebBizException("绑定开放平台账号失败：该开放平台帐号所绑定的公众号/小程序已达上限(100个)");
            }
            throw new WebBizException("绑定开放平台账号失败：" + response.getErrmsg());
        }
    }

    void unbindOpenPlatform(String appId, String refreshToken, String openId) {
        log.debug("unbindOpenPlatform:{} ,appId:{} ,openId:{}", "解绑开放平台", appId, openId);
        OpenAccountAPI api = getOpenAccountAPI(appId, refreshToken);
        BaseResponse response = api.unbind(appId, openId);
        String code1 = "89000";
        String code3 = "89003";
        String code4 = "89004";
        if (!api.isSuccess(response.getErrcode())) {
            if (code1.equals(response.getErrcode())) {
                throw new WebBizException("解绑开放平台账号失败：授权信息与开放平台帐号主体不相同");
            }
            if (code3.equals(response.getErrcode())) {
                throw new WebBizException("解绑开放平台账号失败：该开放平台帐号并非通过api创建，不允许操作");
            }
            if (code4.equals(response.getErrcode())) {
                throw new WebBizException("解绑开放平台账号失败：该开放平台帐号所绑定的公众号/小程序已达上限(100个)");
            }
            throw new WebBizException("解绑开放平台账号失败：" + response.getErrmsg());
        }
    }

    private OpenAccountAPI getOpenAccountAPI(String appId, String refreshToken) {
        AuthorizerApiConfig config = super.getAuthorizerApiConfig(appId, refreshToken);
        if (config == null) {
            throw new WebBizException("获取授权信息失败，无法获取开放平台账号");
        }
        return new OpenAccountAPI(config);
    }

    @Override
    protected RedisTemplate getRedisTemplate() {
        StringRedisTemplate template = new StringRedisTemplate(redisTemplate.getConnectionFactory());
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        template.setHashKeySerializer(jdkSerializationRedisSerializer);
        template.setValueSerializer(jdkSerializationRedisSerializer);
        return template;
    }

    @Override
    protected String getToken() {
        return oauthDynamicPropertyService.getToken();
    }

    @Override
    protected String getAppId() {
        return oauthDynamicPropertyService.getAppid();
    }

    @Override
    protected String getAESKey() {
        return oauthDynamicPropertyService.getKey();
    }

    @Override
    protected String getSecret() {
        return oauthDynamicPropertyService.getSecret();
    }

    @Resource
    private RedisTemplate redisTemplate;

    //private String token = oauthDynamicPropertyService.getToken();

    //private String secret = oauthDynamicPropertyService.getSecret();

    //private String appid = oauthDynamicPropertyService.getAppid();

    //private String key = oauthDynamicPropertyService.getKey();
}
